package com.tyjt.vehdata;

import com.google.common.base.Joiner;
import com.tyjt.common.Constants;
import com.tyjt.common.mqtt.annotation.MqttListener;
import com.tyjt.common.redis.HashTagUtil;
import com.tyjt.proto.PubDataDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 接收算法平台数据
 *
 * @author 种鑫
 * @date 2025/4/4 12:33
 */
@Component
@Slf4j
public class PerceptionListener {
    @Resource
    private RedisTemplate<String, byte[]> binaryRedisTemplate;
    @Resource(name = "redisSaveDataScript")
    private DefaultRedisScript<Long> redisSaveDataScript;

    //  idse/cloud/{alg_id}/{msg_type}
    @MqttListener("$share/idse/cloud/+/+")
    public void perceptionData(String topic, MqttMessage message) {
        try {
            byte[] payload = message.getPayload();

            PubDataDTO.PubData pubData = PubDataDTO.PubData.parseFrom(payload);
            PubDataDTO.MetaData meta = pubData.getMeta();
            //  算法平台
            String companyId = meta.getCompanyId();
            //  数据类型 暂时不用
            String msgType = meta.getMsgType();
            String uid = meta.getUid();
            //  数据单独存储(过期时间)  grid 存储uid
            //  cachekey = companyId:uid
            String cacheKey = Joiner.on(Constants.REDIS_KEY_SEPARATOR).join(companyId, msgType,uid);

            List<String> gridKeys = GridCache.GRID_CACHE.getIfPresent(cacheKey);
            if (gridKeys == null) {
                //  缓存计算
                List<int[]> gridPointList = calculateCoveredGrids(meta.getLongitude(), meta.getLatitude(), meta.getEffectRadiusCm() / 100, Constants.GRID_CELL_LEN);
                // 将坐标点拼接成x-y格式
                gridKeys = gridPointList.stream()
                        .map(point -> point[0] + Constants.REDIS_KEY_GRID_SEPARATOR + point[1])
                        .toList();
                GridCache.GRID_CACHE.put(cacheKey,gridKeys);
            }
            //  相同公司的，相同数据类型，放在一个slot
            String hashTag = HashTagUtil.genHashTag(companyId,msgType);
            // 1. 定义基础键
            String dataKey = Constants.REDIS_DATA_PREFIX + hashTag + Constants.REDIS_KEY_SEPARATOR + uid;      // "data:companyId:uid"
            String gridPrefix = Constants.REDIS_GRID_PREFIX + hashTag  + Constants.REDIS_KEY_SEPARATOR;          // "grid:companyId:"
            String expireSeconds = String.valueOf(Constants.REDIS_EXP_SECONDS); // "3600"

            // 2. 构建键列表（初始容量优化）
            List<String> keys = new ArrayList<>(3 + gridKeys.size());
            keys.add(dataKey);
            keys.add(gridPrefix);
            keys.add(expireSeconds);
            keys.addAll(gridKeys);  // 添加所有坐标键（如 ["x1y1", "x2y2"]）

            List<Object> args = new ArrayList<>();
            args.add(payload);

            Long result = binaryRedisTemplate.execute(redisSaveDataScript, keys, args.toArray());
            System.out.println("Script result: " + result); // 应为1（脚本返回值）

        } catch (Exception e) {
            log.error("",e);
        }

    }


    /**
     * 计算圆覆盖的所有格子（碰到就算，无需中心点）。
     *
     * @param x        当前点经度（WGS84）
     * @param y        当前点纬度（WGS84）
     * @param r        覆盖半径（米）
     * @param gridSize 格子边长（米）
     */
    public static List<int[]> calculateCoveredGrids(double x, double y, double r, int gridSize) {
        double originX = Constants.TIAN_AN_MEN_LON;
        double originY = Constants.TIAN_AN_MEN_LAT;
        //  TODO 转换
        // 1. 将 WGS84 转换为平面坐标（米）
        double deltaX = (x - originX) * Constants.METERS_PER_DEGREE * Math.cos(Math.toRadians(originY));
        double deltaY = (y - originY) * Constants.METERS_PER_DEGREE;

        // 2. 计算圆的外接矩形格子范围
        int minI = (int) Math.floor((deltaX - r) / gridSize);
        int maxI = (int) Math.floor((deltaX + r) / gridSize);
        int minJ = (int) Math.floor((deltaY - r) / gridSize);
        int maxJ = (int) Math.floor((deltaY + r) / gridSize);

        // 3. 遍历所有可能相交的格子
        List<int[]> coveredGrids = new ArrayList<>();
        for (int i = minI; i <= maxI; i++) {
            for (int j = minJ; j <= maxJ; j++) {
                // 计算当前格子的边界（米）
                double left = i * gridSize;
                double right = left + gridSize;
                double top = j * gridSize;
                double bottom = top + gridSize;

                // 判断圆是否与格子相交（碰到就算）
                if (isCircleIntersectingGrid(deltaX, deltaY, r, left, right, top, bottom)) {
                    coveredGrids.add(new int[]{i, j});
                }
            }
        }
        return coveredGrids;
    }

    /**
     * 判断圆是否与格子相交（基于边界碰撞检测）。
     *
     * @param circleX
     * @param circleY
     * @param radius
     * @param left
     * @param right
     * @param top
     * @param bottom
     * @return
     */
    private static boolean isCircleIntersectingGrid(
            double circleX, double circleY, double radius,
            double left, double right, double top, double bottom) {

        // 1. 找到离圆心最近的格子边缘点
        double closestX = clamp(circleX, left, right);
        double closestY = clamp(circleY, top, bottom);

        // 2. 计算该点到圆心的距离
        double distanceX = circleX - closestX;
        double distanceY = circleY - closestY;
        double distanceSquared = distanceX * distanceX + distanceY * distanceY;

        // 3. 如果距离 ≤ 半径，则圆与格子相交
        return distanceSquared <= (radius * radius);
    }

    /**
     * 将值限制在 [min, max] 范围内。
     *
     * @param value
     * @param min
     * @param max
     * @return
     */
    private static double clamp(double value, double min, double max) {
        return Math.max(min, Math.min(max, value));
    }


}
